﻿using System;
using System.Collections.Generic;
using Autofac;
using Autofac.Core.Lifetime;
using Nancy;
using Nancy.Bootstrapper;
using Nancy.Diagnostics;

namespace QQ2564874169.WebFx.Nancy
{
    public class NancyBootstrapper : NancyBootstrapperWithRequestContainerBase<ILifetimeScope>
    {
        protected override IDiagnostics GetDiagnostics()
        {
            return ApplicationContainer.Resolve<IDiagnostics>();
        }

        protected override IEnumerable<IApplicationStartup> GetApplicationStartupTasks()
        {
            return ApplicationContainer.Resolve<IEnumerable<IApplicationStartup>>();
        }

        protected override IEnumerable<IRequestStartup> RegisterAndGetRequestStartupTasks(ILifetimeScope container,
            Type[] requestStartupTypes)
        {
            container.Update(builder =>
            {
                foreach (var requestStartupType in requestStartupTypes)
                {
                    builder.RegisterType(requestStartupType)
                        .As<IRequestStartup>()
                        .PreserveExistingDefaults()
                        .InstancePerDependency();
                }
            });

            return container.Resolve<IEnumerable<IRequestStartup>>();
        }

        protected override IEnumerable<IRegistrations> GetRegistrationTasks()
        {
            return ApplicationContainer.Resolve<IEnumerable<IRegistrations>>();
        }

        protected override INancyEngine GetEngineInternal()
        {
            return ApplicationContainer.Resolve<INancyEngine>();
        }

        protected override ILifetimeScope GetApplicationContainer()
        {
            return new ContainerBuilder().Build();
        }

        protected override void RegisterBootstrapperTypes(ILifetimeScope applicationContainer)
        {
            applicationContainer.Update(
                builder => builder.RegisterInstance(this).As<INancyModuleCatalog>());
        }

        protected override void RegisterTypes(ILifetimeScope container, IEnumerable<TypeRegistration> typeRegistrations)
        {
            container.Update(builder =>
            {
                foreach (var typeRegistration in typeRegistrations)
                {
                    switch (typeRegistration.Lifetime)
                    {
                        case Lifetime.Transient:
                            builder.RegisterType(typeRegistration.ImplementationType)
                                .As(typeRegistration.RegistrationType)
                                .InstancePerDependency();
                            break;
                        case Lifetime.Singleton:
                            builder.RegisterType(typeRegistration.ImplementationType)
                                .As(typeRegistration.RegistrationType)
                                .SingleInstance();
                            break;
                        case Lifetime.PerRequest:
                            builder.RegisterType(typeRegistration.ImplementationType)
                                .As(typeRegistration.RegistrationType)
                                .InstancePerLifetimeScope();
                            break;
                        default:
                            throw new ArgumentOutOfRangeException();
                    }
                }
            });
        }

        protected override void RegisterCollectionTypes(ILifetimeScope container,
            IEnumerable<CollectionTypeRegistration> collectionTypeRegistrations)
        {
            container.Update(builder =>
            {
                foreach (var collectionTypeRegistration in collectionTypeRegistrations)
                {
                    foreach (var implementationType in collectionTypeRegistration.ImplementationTypes)
                    {
                        switch (collectionTypeRegistration.Lifetime)
                        {
                            case Lifetime.Transient:
                                builder.RegisterType(implementationType)
                                    .As(collectionTypeRegistration.RegistrationType)
                                    .PreserveExistingDefaults()
                                    .InstancePerDependency();
                                break;
                            case Lifetime.Singleton:
                                builder.RegisterType(implementationType)
                                    .As(collectionTypeRegistration.RegistrationType)
                                    .PreserveExistingDefaults()
                                    .SingleInstance();
                                break;
                            case Lifetime.PerRequest:
                                builder.RegisterType(implementationType)
                                    .As(collectionTypeRegistration.RegistrationType)
                                    .PreserveExistingDefaults()
                                    .InstancePerLifetimeScope();
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }
                }
            });
        }

        protected override void RegisterInstances(ILifetimeScope container,
            IEnumerable<InstanceRegistration> instanceRegistrations)
        {
            container.Update(builder =>
            {
                foreach (var instanceRegistration in instanceRegistrations)
                {
                    builder.RegisterInstance(instanceRegistration.Implementation)
                        .As(instanceRegistration.RegistrationType);
                }
            });
        }

        protected override ILifetimeScope CreateRequestContainer(NancyContext context)
        {
            return ApplicationContainer.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
        }

        protected override void RegisterRequestContainerModules(ILifetimeScope container,
            IEnumerable<ModuleRegistration> moduleRegistrationTypes)
        {
            container.Update(builder =>
            {
                foreach (var item in moduleRegistrationTypes)
                {
                    builder.RegisterType(item.ModuleType).As<INancyModule>();
                }
            });
        }

        protected override IEnumerable<INancyModule> GetAllModules(ILifetimeScope container)
        {
            return container.Resolve<IEnumerable<INancyModule>>();
        }

        protected override INancyModule GetModule(ILifetimeScope container, Type moduleType)
        {
            if (container.IsRegistered(moduleType) == false)
                container.Update(builder => builder.RegisterType(moduleType).AsSelf());
            return container.Resolve(moduleType) as INancyModule;
        }
    }
}
